package workflow

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/asynkron/protoactor-go/actor"
	"github.com/google/uuid"
	"github.com/louloulin/dataflare/pkg/actor/system"
)

// Engine is the workflow execution engine
type Engine struct {
	// actorSystem is the actor system
	actorSystem *system.ActorSystem
	// workflowSupervisor is the workflow supervisor PID
	workflowSupervisor *actor.PID
	// mu is a mutex for protecting the engine
	mu sync.RWMutex
}

// NewEngine creates a new workflow engine
func NewEngine() (*Engine, error) {
	// Create actor system
	actorSystem, err := system.NewActorSystem()
	if err != nil {
		return nil, fmt.Errorf("failed to create actor system: %w", err)
	}

	// Get workflow supervisor
	future := actorSystem.System.Root.RequestFuture(actorSystem.SystemSupervisor, &system.GetChildrenCommand{}, 5*time.Second)
	result, err := future.Result()
	if err != nil {
		return nil, fmt.Errorf("failed to get system supervisor children: %w", err)
	}

	response, ok := result.(*system.GetChildrenResponse)
	if !ok {
		return nil, fmt.Errorf("unexpected response type: %T", result)
	}

	workflowSupervisor := response.Children["workflow-supervisor"]
	if workflowSupervisor == nil {
		return nil, fmt.Errorf("workflow supervisor not found")
	}

	return &Engine{
		actorSystem:       actorSystem,
		workflowSupervisor: workflowSupervisor,
	}, nil
}

// DeployWorkflow deploys a workflow
func (e *Engine) DeployWorkflow(ctx context.Context, definition *Definition) (string, error) {
	e.mu.Lock()
	defer e.mu.Unlock()

	// Generate workflow ID if not provided
	workflowID := definition.Name
	if workflowID == "" {
		workflowID = uuid.New().String()
	}

	// Deploy workflow
	future := e.actorSystem.System.Root.RequestFuture(e.workflowSupervisor, &DeployWorkflowCommand{
		WorkflowID: workflowID,
		Definition: definition,
	}, 30*time.Second)

	result, err := future.Result()
	if err != nil {
		return "", fmt.Errorf("failed to deploy workflow: %w", err)
	}

	response, ok := result.(*DeployWorkflowResponse)
	if !ok {
		return "", fmt.Errorf("unexpected response type: %T", result)
	}

	if !response.Success {
		return "", fmt.Errorf("deployment failed: %s", response.Error)
	}

	return workflowID, nil
}

// StartWorkflow starts a workflow
func (e *Engine) StartWorkflow(ctx context.Context, workflowID string, parameters map[string]interface{}) (string, error) {
	e.mu.Lock()
	defer e.mu.Unlock()

	// Start workflow
	future := e.actorSystem.System.Root.RequestFuture(e.workflowSupervisor, &StartWorkflowCommand{
		WorkflowID: workflowID,
		Parameters: parameters,
	}, 30*time.Second)

	result, err := future.Result()
	if err != nil {
		return "", fmt.Errorf("failed to start workflow: %w", err)
	}

	response, ok := result.(*StartWorkflowResponse)
	if !ok {
		return "", fmt.Errorf("unexpected response type: %T", result)
	}

	if !response.Success {
		return "", fmt.Errorf("start failed: %s", response.Error)
	}

	return response.RunID, nil
}

// StopWorkflow stops a workflow
func (e *Engine) StopWorkflow(ctx context.Context, runID string) error {
	e.mu.Lock()
	defer e.mu.Unlock()

	// Stop workflow
	future := e.actorSystem.System.Root.RequestFuture(e.workflowSupervisor, &StopWorkflowCommand{
		RunID: runID,
	}, 30*time.Second)

	result, err := future.Result()
	if err != nil {
		return fmt.Errorf("failed to stop workflow: %w", err)
	}

	response, ok := result.(*StopWorkflowResponse)
	if !ok {
		return fmt.Errorf("unexpected response type: %T", result)
	}

	if !response.Success {
		return fmt.Errorf("stop failed: %s", response.Error)
	}

	return nil
}

// GetWorkflowStatus gets the status of a workflow
func (e *Engine) GetWorkflowStatus(ctx context.Context, runID string) (*WorkflowStatus, error) {
	e.mu.RLock()
	defer e.mu.RUnlock()

	// Get workflow status
	future := e.actorSystem.System.Root.RequestFuture(e.workflowSupervisor, &GetWorkflowStatusCommand{
		RunID: runID,
	}, 30*time.Second)

	result, err := future.Result()
	if err != nil {
		return nil, fmt.Errorf("failed to get workflow status: %w", err)
	}

	response, ok := result.(*GetWorkflowStatusResponse)
	if !ok {
		return nil, fmt.Errorf("unexpected response type: %T", result)
	}

	if !response.Success {
		return nil, fmt.Errorf("get status failed: %s", response.Error)
	}

	return &WorkflowStatus{
		RunID:        runID,
		Status:       response.Status,
		Progress:     response.Progress,
		StartTime:    response.StartTime,
		EndTime:      response.EndTime,
		NodeStatuses: response.NodeStatuses,
	}, nil
}

// ListWorkflows lists all workflows
func (e *Engine) ListWorkflows(ctx context.Context) ([]*WorkflowInfo, error) {
	e.mu.RLock()
	defer e.mu.RUnlock()

	// List workflows
	future := e.actorSystem.System.Root.RequestFuture(e.workflowSupervisor, &ListWorkflowsCommand{}, 30*time.Second)

	result, err := future.Result()
	if err != nil {
		return nil, fmt.Errorf("failed to list workflows: %w", err)
	}

	response, ok := result.(*ListWorkflowsResponse)
	if !ok {
		return nil, fmt.Errorf("unexpected response type: %T", result)
	}

	return response.Workflows, nil
}

// ListWorkflowInstances lists workflow instances
func (e *Engine) ListWorkflowInstances(ctx context.Context, workflowID string) ([]*WorkflowInstanceInfo, error) {
	e.mu.RLock()
	defer e.mu.RUnlock()

	// List workflow instances
	future := e.actorSystem.System.Root.RequestFuture(e.workflowSupervisor, &ListWorkflowInstancesCommand{
		WorkflowID: workflowID,
	}, 30*time.Second)

	result, err := future.Result()
	if err != nil {
		return nil, fmt.Errorf("failed to list workflow instances: %w", err)
	}

	response, ok := result.(*ListWorkflowInstancesResponse)
	if !ok {
		return nil, fmt.Errorf("unexpected response type: %T", result)
	}

	return response.Instances, nil
}

// Shutdown shuts down the engine
func (e *Engine) Shutdown() error {
	e.mu.Lock()
	defer e.mu.Unlock()

	// Shutdown actor system
	return e.actorSystem.Shutdown()
}

// WorkflowStatus represents the status of a workflow
type WorkflowStatus struct {
	// RunID is the workflow run ID
	RunID string
	// Status is the workflow status
	Status string
	// Progress is the workflow progress (0-100)
	Progress int
	// StartTime is when the workflow started
	StartTime time.Time
	// EndTime is when the workflow ended
	EndTime time.Time
	// NodeStatuses are the statuses of the workflow nodes
	NodeStatuses map[string]string
}

// DeployWorkflowCommand is a command to deploy a workflow
type DeployWorkflowCommand struct {
	// WorkflowID is the workflow ID
	WorkflowID string
	// Definition is the workflow definition
	Definition interface{}
}

// DeployWorkflowResponse is the response to a DeployWorkflowCommand
type DeployWorkflowResponse struct {
	// Success indicates if the operation was successful
	Success bool
	// Error is the error message if the operation failed
	Error string
}

// StartWorkflowCommand is a command to start a workflow
type StartWorkflowCommand struct {
	// WorkflowID is the workflow ID
	WorkflowID string
	// Parameters are the workflow parameters
	Parameters map[string]interface{}
}

// StartWorkflowResponse is the response to a StartWorkflowCommand
type StartWorkflowResponse struct {
	// Success indicates if the operation was successful
	Success bool
	// Error is the error message if the operation failed
	Error string
	// RunID is the ID of the workflow run
	RunID string
}

// StopWorkflowCommand is a command to stop a workflow
type StopWorkflowCommand struct {
	// RunID is the ID of the workflow run
	RunID string
}

// StopWorkflowResponse is the response to a StopWorkflowCommand
type StopWorkflowResponse struct {
	// Success indicates if the operation was successful
	Success bool
	// Error is the error message if the operation failed
	Error string
}

// GetWorkflowStatusCommand is a command to get workflow status
type GetWorkflowStatusCommand struct {
	// RunID is the ID of the workflow run
	RunID string
}

// GetWorkflowStatusResponse is the response to a GetWorkflowStatusCommand
type GetWorkflowStatusResponse struct {
	// Success indicates if the operation was successful
	Success bool
	// Error is the error message if the operation failed
	Error string
	// Status is the workflow status
	Status string
	// Progress is the workflow progress (0-100)
	Progress int
	// StartTime is when the workflow started
	StartTime time.Time
	// EndTime is when the workflow ended
	EndTime time.Time
	// NodeStatuses are the statuses of the workflow nodes
	NodeStatuses map[string]string
}

// ListWorkflowsCommand is a command to list workflows
type ListWorkflowsCommand struct{}

// ListWorkflowsResponse is the response to a ListWorkflowsCommand
type ListWorkflowsResponse struct {
	// Workflows are the workflows
	Workflows []*WorkflowInfo
}

// WorkflowInfo represents workflow information
type WorkflowInfo struct {
	// ID is the workflow ID
	ID string
	// Name is the workflow name
	Name string
	// Description is the workflow description
	Description string
	// Version is the workflow version
	Version string
}

// ListWorkflowInstancesCommand is a command to list workflow instances
type ListWorkflowInstancesCommand struct {
	// WorkflowID is the workflow ID to filter by
	WorkflowID string
}

// ListWorkflowInstancesResponse is the response to a ListWorkflowInstancesCommand
type ListWorkflowInstancesResponse struct {
	// Instances are the workflow instances
	Instances []*WorkflowInstanceInfo
}

// WorkflowInstanceInfo represents workflow instance information
type WorkflowInstanceInfo struct {
	// ID is the instance ID
	ID string
	// WorkflowID is the workflow ID
	WorkflowID string
	// Status is the instance status
	Status string
	// StartTime is when the instance started
	StartTime time.Time
	// EndTime is when the instance ended
	EndTime time.Time
	// Error is the error if the instance failed
	Error string
}
